<template>
  <div>手写代码</div>
</template>
<script setup>
function isObject(obj) {
  return obj !== null && typeof obj === 'object';
}

/** 手写深拷贝 */
function deepClone(data, hash = new WeakMap()) { // 加入hash表，解决循环引用问题
  // 判断是否对象
  if( !isObject(data)) {
    return data
  }
  // 如果已经有拷贝过的对象，直接返回
  if(hash.has(data)) {
    return hash.get(data)
  }
  // 创建变量用于接收赋值
  const result = Array.isArray(data) ? [] : {}

  // 解决循环引用问题，将当前对象和拷贝后的对象加入hash表
  hash.set(data, result)

  // 遍历target
  for(let key in data) {
    if(isObject(data[key])) {
      deepClone(data[key])
    } else {
      result[key] = data[key]
    }
  }

  return result
}

/** 手写防抖函数 */
// 最简单的防抖函数
// 缺陷：不能处理this指向和event对象
function babyDebounce(fn, delay) {
  let timer = null
  return function () {
    if(timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(fn, delay)
  }
}
// 进阶：保留this指向和event对象
function debounce(fn, delay) {
  let timer = null
  return function(...args) {
    // args将传参给fn
    if(timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      // apply方法将this指向调用debounce函数的对象，args将传参给fn
      fn.apply(this, args)
    }, delay)
  }
}

/** 手写节流函数 */
function throttle(fn, delay) {
  let lastTime = 0
  return function(...args) {
    const nowTime = Date.now()
    if(nowTime - lastTime >= delay) {
      fn.apply(this, args)
      lastTime = nowTime
    }
  }
}


/** 手写Promise */
// 思路：
// 1. 有三种状态：pending、resolved、rejected
// 2. 状态只能从pending到resolved或rejected，不能逆向操作
// 3. new Promise时会接收一个执行器 executor 并立即执行，执行器会有两个参数：resolve和reject
// 4. then方法：接收两个回调函数，成功执行第一个函数：onFulfilled，失败执行第二个函数：onRejected
// 定义状态常量
const PENDING = 'pending'
const RESOLVED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
  constructor(executor) {
    // 初始化状态为pending
    this.status = PENDING // 初始状态为pending
    this.successValue = null // 成功时的值
    this.failValue = null // 失败时的值
    // 成功时改变status为resolved，将成功时的值赋值给successValue
    let resolve = value => {
      if(this.status !== PENDING) return
      this.status = RESOLVED
      this.successValue = value
    } 
    // 失败时改变status为rejected，将失败时的值赋值给failValue
    let reject = value => {
      if(this.status !== PENDING) return
      this.status = REJECTED
      this.failValue = value
    }

    /** 立即执行 */
    try {
      executor(resolve, reject)
    } catch(err) {
      reject(err)
    }
  }
  // 这里根据状态变化执行不同的回调函数
  then(onFulfilled, onRejected) {
    if(this.status === RESOLVED) {
      onFulfilled(this.successValue)
    } else if(this.status === REJECTED) {
      onRejected(this.failValue)
    }
  }
}

// 测试， 这里只实现了同步的情况，接下来要考虑异步的情况
const promise = new MyPromise((resolve, reject) => {
    resolve('成功')
    // reject('失败')
})
promise.then(res => {
  console.log('promise1',res)
})

/** Promise 异步情况 */
// 思路：
// 1. 现在异步不行的原因是then调用的时候，异步的resolve、reject还没有执行，所以状态还是pending
// 2. 解决方法：在then方法执行时判断status，如果是pending，将传入的回调函数存储起来，等异步执行完再调用
// 3. 如果是resolved或rejected状态，直接调用回调函数
class MyPromise2 {
  constructor(executor) {
    // 初始化状态为pending
    this.status = PENDING // 初始状态为pending
    this.successValue = null // 成功时的值
    this.failValue = null // 失败时的值
    // 存放成功和失败的回调函数数组
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
    // 成功时改变status为resolved，将成功时的值赋值给successValue
    let resolve = value => {
      if(this.status !== PENDING) return
      this.status = RESOLVED
      this.successValue = value
      this.onFulfilledCallbacks.forEach(callback => callback(this.successValue))
    } 
    // 失败时改变status为rejected，将失败时的值赋值给failValue
    let reject = value => {
      if(this.status !== PENDING) return
      this.status = REJECTED
      this.failValue = value
      this.onRejectedCallbacks.forEach(callback => callback(this.failValue))
    }

    /** 立即执行 */
    try {
      executor(resolve, reject)
    } catch(err) {
      reject(err)
    }
  }
  // 这里根据状态变化执行不同的回调函数
  then(onFulfilled, onRejected) {
    if(this.status === RESOLVED) {
      onFulfilled(this.successValue)
    } else if(this.status === REJECTED) {
      onRejected(this.failValue)
    }
    // 如果是pending状态，将回调函数存储起来
    else if(this.status === PENDING) {
      this.onFulfilledCallbacks.push(() => onFulfilled(this.successValue))
      this.onRejectedCallbacks.push(() => onRejected(this.failValue))
    }
  }
}

// 测试异步情况
const promise2 = new MyPromise2((resolve, reject) => {
  setTimeout(() => {
    resolve('成功')
  }, 1000)
})
promise2.then(res => {
  console.log('promise2',res)
})

class IPromise {
  // 设置私有属性
  #status = PENDING // 执行状态
  #result = undefined // 执行结果
  #thenCallbacks = [] // then方法的回调函数数组
  
  constructor(executor) {
    // 成功时改变status为resolved，将成功时的值赋值给result
    let resolve = data => {
      this.#changeStatus(RESOLVED, data)
    } 
    // 失败时改变status为rejected，将失败时的值赋值给result
    let reject = err => {
      this.#changeStatus(REJECTED, err)
    }
    try {
      executor(resolve, reject)
    } catch(err) {
      reject(err)
    }
  }

  #changeStatus(status, result) {
    if(this.#status !== PENDING) return
    this.#status = status
    this.#result = result
    // 执行队列中的回调函数
    this.#run()
  }

  // 执行callback
  #handleCallback(callback, resolve, reject) {
    // 判断callback是否为函数
    if(typeof callback !== 'function') {
      // 如果不是函数则将上一个Promise的结果作为下一个Promise的入参
      queueMicrotask(() => {
        callback = this.#status === RESOLVED ? resolve : reject
        callback(this.#result)
      })
      return
    }
    // 加入微队列
    queueMicrotask(() => {
      try {
        const result = callback(this.#result)
        resolve(result)
      } catch(err) {
        reject(err)
      }
    })
    
  }

  // 执行队列的方法
  #run() {
    // 判断状态是否为fulfilled或rejected
    if(this.#status === PENDING) return
    // 按顺序执行队列
    while(this.#thenCallbacks.length > 0) {
      // 取出队列中的第一个回调函数
      const {onFulfilled, onRejected, resolve, reject} = this.#thenCallbacks.shift()
      // 判断状态后执行对应的方法
      if(this.#status === RESOLVED) {
        this.#handleCallback(onFulfilled, resolve, reject)
      } else if(this.#status === REJECTED) {
        this.#handleCallback(onRejected, resolve, reject)
      }
    } 
  }

  // then方法
  then(onFulfilled, onRejected) {
    return new IPromise((resolve, reject) => {
      // 这里加入队列
      this.#thenCallbacks.push({
        onFulfilled,
        onRejected,
        resolve,
        reject
      })

      // 启动队列处理
      this.#run()
    })
  }
}

// 测试异步情况
const promise5 = new IPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功')
  }, 1000)
})
promise5.then(res => {
  console.log('promise5',res)
  return res + '22'
})
.then(res => {
  console.log('promise52',res)
})


</script>
<style>
</style>